1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 import java.io.IOException;
30
31 import javax.sound.midi.*;
32 import javax.sound.sampled.*;
33
34 import com.sun.media.sound.*;
35
36 public class RealTimeTuning {
37
38 private static class PitchSpy {
39 public float pitch = 0;
40
41 public Soundbank getSoundBank() {
42 ModelOscillator osc = new ModelOscillator() {
43 public float getAttenuation() {
44 return 0;
45 }
46
47 public int getChannels() {
48 return 0;
49 }
50
51 public ModelOscillatorStream open(float samplerate) {
52 return new ModelOscillatorStream() {
53 public void close() throws IOException {
54 pitch = 0;
55 }
56
57 public void noteOff(int velocity) {
58 pitch = 0;
59 }
60
61 public void noteOn(MidiChannel channel,
62 VoiceStatus voice, int noteNumber, int velocity) {
63 pitch = noteNumber * 100;
64 }
65
66 public int read(float[][] buffer, int offset, int len)
67 throws IOException {
68 return len;
69 }
70
71 public void setPitch(float ipitch) {
72 pitch = ipitch;
73 }
74 };
75 }
76 };
77 ModelPerformer performer = new ModelPerformer();
78 performer.getOscillators().add(osc);
79 SimpleInstrument testinstrument = new SimpleInstrument();
80 testinstrument.setPatch(new Patch(0, 0));
81 testinstrument.add(performer);
82 SimpleSoundbank testsoundbank = new SimpleSoundbank();
83 testsoundbank.addInstrument(testinstrument);
84 return testsoundbank;
85 }
86 }
87
88 public static void sendTuningChange(Receiver recv, int channel,
89 int tuningpreset, int tuningbank) throws InvalidMidiDataException {
90
91 ShortMessage sm1 = new ShortMessage();
92 sm1.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x64, 04);
93 ShortMessage sm2 = new ShortMessage();
94 sm2.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x65, 00);
95
96
97 ShortMessage sm3 = new ShortMessage();
98 sm3.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x06, tuningbank);
99
100 ShortMessage sm4 = new ShortMessage();
101 sm4.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x60, 0x7F);
102
103 ShortMessage sm5 = new ShortMessage();
104 sm5.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x61, 0x7F);
105
106
107 ShortMessage sm6 = new ShortMessage();
108 sm6.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x64, 03);
109 ShortMessage sm7 = new ShortMessage();
110 sm7.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x65, 00);
111
112
113 ShortMessage sm8 = new ShortMessage();
114 sm8
115 .setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x06,
116 tuningpreset);
117
118 ShortMessage sm9 = new ShortMessage();
119 sm9.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x60, 0x7F);
120
121 ShortMessage sm10 = new ShortMessage();
122 sm10.setMessage(ShortMessage.CONTROL_CHANGE, channel, 0x61, 0x7F);
123
124 recv.send(sm1, -1);
125 recv.send(sm2, -1);
126 recv.send(sm3, -1);
127 recv.send(sm4, -1);
128 recv.send(sm5, -1);
129 recv.send(sm6, -1);
130 recv.send(sm7, -1);
131 recv.send(sm8, -1);
132 recv.send(sm9, -1);
133 recv.send(sm10, -1);
134
135 }
136
137 private static void assertTrue(boolean value) throws Exception {
138 if (!value)
139 throw new RuntimeException("assertTrue fails!");
140 }
141
142 public static void testTunings(int[] msg, int tuningProgram,
143 int tuningBank, int targetNote, float targetPitch, boolean realtime)
144 throws Exception {
145 AudioSynthesizer synth = new SoftSynthesizer();
146 AudioInputStream stream = synth.openStream(null, null);
147 Receiver recv = synth.getReceiver();
148 MidiChannel channel = synth.getChannels()[0];
149 byte[] buff = new byte[2048];
150
151
152 PitchSpy pitchspy = new PitchSpy();
153
154 synth.unloadAllInstruments(synth.getDefaultSoundbank());
155 synth.loadAllInstruments(pitchspy.getSoundBank());
156
157 SysexMessage sysex = null;
158
159
160 if (msg != null) {
161 byte[] bmsg = new byte[msg.length];
162 for (int i = 0; i < bmsg.length; i++)
163 bmsg[i] = (byte) msg[i];
164 sysex = new SysexMessage();
165 sysex.setMessage(bmsg, bmsg.length);
166 if (targetPitch == 0) {
167 targetPitch = (float) new SoftTuning(bmsg)
168 .getTuning(targetNote);
169
170 assertTrue(Math.abs(targetPitch - targetNote * 100.0) > 0.001);
171 }
172 }
173
174 if (tuningProgram != -1)
175 sendTuningChange(recv, 0, tuningProgram, tuningBank);
176
177
178 channel.noteOn(targetNote, 64);
179 stream.read(buff, 0, buff.length);
180 assertTrue(Math.abs(pitchspy.pitch - (targetNote * 100.0)) < 0.001);
181
182
183 if (sysex != null)
184 recv.send(sysex, -1);
185 stream.read(buff, 0, buff.length);
186 if (realtime)
187 assertTrue(Math.abs(pitchspy.pitch - targetPitch) < 0.001);
188 else
189 assertTrue(Math.abs(pitchspy.pitch - (targetNote * 100.0)) < 0.001);
190
191
192 channel.noteOn(targetNote, 0);
193 stream.read(buff, 0, buff.length);
194 assertTrue(Math.abs(pitchspy.pitch - 0.0) < 0.001);
195
196 channel.noteOn(targetNote, 64);
197 stream.read(buff, 0, buff.length);
198 assertTrue(Math.abs(pitchspy.pitch - targetPitch) < 0.001);
199
200 channel.noteOn(targetNote, 0);
201 stream.read(buff, 0, buff.length);
202 assertTrue(Math.abs(pitchspy.pitch - 0.0) < 0.001);
203
204 stream.close();
205 }
206
207 public static void main(String[] args) throws Exception {
208
209 testTunings(null, -1, -1, 60, 6000, false);
210
211 int[] msg;
212
213 msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x02, 0x10, 0x02, 36, 36, 64,
214 0, 60, 70, 0, 0, 0xf7 };
215 testTunings(msg, 0x10, 0, 60, 7000, true);
216
217
218 msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x07, 0x05, 0x07, 0x02, 36,
219 36, 64, 0, 60, 80, 0, 0, 0xf7 };
220 testTunings(msg, 0x07, 0x05, 60, 8000, false);
221
222
223 msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x07, 0x05, 0x07, 0x02, 36,
224 36, 64, 0, 60, 80, 0, 0, 0xf7 };
225 testTunings(msg, 0x07, 0x05, 60, 8000, true);
226
227
228 msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x08, 0x03, 0x7f, 0x7f, 5,
229 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 0xf7 };
230 testTunings(msg, -1, -1, 60, 0, false);
231
232
233 msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x08, 0x03, 0x7f, 0x7f, 5,
234 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 0xf7 };
235 testTunings(msg, -1, -1, 60, 0, true);
236
237
238 msg = new int[] { 0xf0, 0x7e, 0x7f, 0x08, 0x09, 0x03, 0x7f, 0x7f, 5,
239 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 5, 10, 15, 20, 25,
240 30, 35, 40, 45, 50, 51, 52, 0xf7 };
241 testTunings(msg, -1, -1, 60, 0, false);
242
243
244 msg = new int[] { 0xf0, 0x7f, 0x7f, 0x08, 0x09, 0x03, 0x7f, 0x7f, 5,
245 10, 15, 20, 25, 30, 35, 40, 45, 50, 51, 52, 5, 10, 15, 20, 25,
246 30, 35, 40, 45, 50, 51, 52, 0xf7 };
247 testTunings(msg, -1, -1, 60, 0, true);
248
249 }
250 }